/*=============================================================================
  CPUDetect.cpp : CPU detection.
  Copyright 2001 Crytek Studios. All Rights Reserved.

  Revision history:
    * Created by Honitch Andrey

=============================================================================*/

#ifndef __CPUDETECT_H__
#define __CPUDETECT_H__

//-------------------------------------------------------
/// Cpu class
//-------------------------------------------------------


#if defined(WIN64)
	#define MAX_CPU 64
#else
	#define MAX_CPU	32
#endif

/// Cpu Features
#define CFI_FPUEMULATION 1
#define CFI_MMX   2
#define CFI_3DNOW 4
#define CFI_SSE   8
#define CFI_SSE2  0x10

/// Type of Cpu Vendor.
enum ECpuVendor
{
  eCVendor_Unknown,
  eCVendor_Intel,
  eCVendor_Cyrix,
  eCVendor_AMD,
  eCVendor_Centaur,
  eCVendor_NexGen,
  eCVendor_UMC,
  eCVendor_M68K
};

/// Type of Cpu Model.
enum ECpuModel
{
  eCpu_Unknown,

  eCpu_8086,
  eCpu_80286,
  eCpu_80386,
  eCpu_80486,
  eCpu_Pentium,
  eCpu_PentiumPro,
  eCpu_Pentium2,
  eCpu_Pentium3,
  eCpu_Pentium4,
  eCpu_Pentium2Xeon,
  eCpu_Pentium3Xeon,
  eCpu_Celeron,
  eCpu_CeleronA,

  eCpu_Am5x86,
  eCpu_AmK5,
  eCpu_AmK6,
  eCpu_AmK6_2,
  eCpu_AmK6_3,
  eCpu_AmK6_3D,
  eCpu_AmAthlon,
  eCpu_AmDuron,

  eCpu_CyrixMediaGX,
  eCpu_Cyrix6x86,
  eCpu_CyrixGXm,
  eCpu_Cyrix6x86MX,

  eCpu_CenWinChip,
  eCpu_CenWinChip2,
};

struct SCpu
{
  ECpuVendor meVendor;
  ECpuModel meModel; 
  unsigned long mFeatures;
  bool mbSerialPresent;
  char  mSerialNumber[30];
  int mFamily;
  int mModel;
  int mStepping;
  char mVendor[64];
  char mCpuType[64];
  char mFpuType[64];
  int mSpeed;
	bool mbPhysical; // false for hyperthreaded
	DWORD_PTR mAffinityMask;
  double m_SecondsPerCycle;

	// constructor
	SCpu() 
		:meVendor(eCVendor_Unknown), meModel(eCpu_Unknown), mFeatures(0), 
		mbSerialPresent(false), mFamily(0), mModel(0), mStepping(0), mSpeed(0), 
		mbPhysical(true), mAffinityMask(0), m_SecondsPerCycle(0)
	{
		memset(mSerialNumber,0,sizeof(mSerialNumber));
		memset(mVendor,0,sizeof(mVendor));
		memset(mCpuType,0,sizeof(mCpuType));
		memset(mFpuType,0,sizeof(mFpuType));
	}
};

class CCpuFeatures
{
private:
	int m_NumLogicalProcessors;
  int m_NumSystemProcessors;
  int m_NumAvailProcessors;
	int m_NumPhysicsProcessors;
  bool m_bOS_ISSE;
  bool m_bOS_ISSE_EXCEPTIONS;
public:

  SCpu m_Cpu[MAX_CPU];

public:
  CCpuFeatures()
  {
		m_NumLogicalProcessors = 0;
    m_NumSystemProcessors = 0;
    m_NumAvailProcessors  = 0;
		m_NumPhysicsProcessors = 0;
    m_bOS_ISSE = 0;
    m_bOS_ISSE_EXCEPTIONS = 0;
		ZeroMemory(m_Cpu, sizeof(m_Cpu));
  }
  
  void Detect(void);
  bool hasSSE() { return (m_Cpu[0].mFeatures & CFI_SSE) != 0; }
  bool hasSSE2() { return (m_Cpu[0].mFeatures & CFI_SSE2) != 0; }
  bool has3DNow() { return (m_Cpu[0].mFeatures & CFI_3DNOW) != 0; }
  bool hasMMX() { return (m_Cpu[0].mFeatures & CFI_MMX) != 0; }

	unsigned int GetLogicalCPUCount() { return m_NumLogicalProcessors; }
	unsigned int GetPhysCPUCount() { return m_NumPhysicsProcessors; }
	unsigned int GetCPUCount() { return m_NumAvailProcessors; }
	DWORD_PTR GetCPUAffinityMask(unsigned int iCPU) { assert(iCPU < MAX_CPU); return iCPU<GetCPUCount() ? m_Cpu[iCPU].mAffinityMask : 0; }
	DWORD_PTR GetPhysCPUAffinityMask(unsigned int iCPU) 
	{
		if (iCPU>GetPhysCPUCount())
			return 0;
		int i;
		for(i=0; (int)iCPU>=0; i++) if (m_Cpu[i].mbPhysical)
			--iCPU;
		return m_Cpu[i-1].mAffinityMask;
	}
};

#endif // #ifndef __CPUDETECT_H__
